<!--
  ~ Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Fetcher EventStream Examples</title>
    <style>
      :root {
        --primary-color: #007acc;
        --primary-hover: #005a9e;
        --success-color: #388e3c;
        --error-color: #d32f2f;
        --warning-color: #f57c00;
        --background-color: #f5f5f5;
        --card-background: #ffffff;
        --text-color: #333333;
        --border-color: #eeeeee;
        --shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      }

      body {
        font-family:
          -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
          Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
        max-width: 1200px;
        margin: 0 auto;
        padding: 2rem;
        background-color: var(--background-color);
        color: var(--text-color);
      }

      .header {
        text-align: center;
        margin-bottom: 2rem;
      }

      .header h1 {
        color: var(--text-color);
        margin-bottom: 0.5rem;
      }

      .header p {
        color: #666;
        font-size: 1.1rem;
      }

      .container {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
        gap: 2rem;
      }

      @media (max-width: 768px) {
        .container {
          grid-template-columns: 1fr;
        }
      }

      .example-card {
        background: var(--card-background);
        border-radius: 8px;
        padding: 1.5rem;
        box-shadow: var(--shadow);
        transition:
          transform 0.2s ease,
          box-shadow 0.2s ease;
      }

      .example-card:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
      }

      .example-card h2 {
        color: var(--text-color);
        margin-top: 0;
        border-bottom: 2px solid var(--border-color);
        padding-bottom: 0.5rem;
      }

      .controls {
        display: flex;
        gap: 1rem;
        flex-wrap: wrap;
        margin-bottom: 1rem;
      }

      button {
        background-color: var(--primary-color);
        color: white;
        border: none;
        padding: 0.5rem 1rem;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9rem;
        transition: background-color 0.2s ease;
      }

      button:hover:not(:disabled) {
        background-color: var(--primary-hover);
      }

      button:disabled {
        background-color: #cccccc;
        cursor: not-allowed;
      }

      button.secondary {
        background-color: #6c757d;
      }

      button.secondary:hover:not(:disabled) {
        background-color: #5a6268;
      }

      .output {
        margin-top: 1rem;
        padding: 1rem;
        background-color: #f8f9fa;
        border-radius: 4px;
        white-space: pre-wrap;
        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
        max-height: 300px;
        overflow-y: auto;
        font-size: 0.85rem;
        border: 1px solid var(--border-color);
      }

      .output.loading {
        color: var(--primary-color);
      }

      .output.error {
        color: var(--error-color);
        background-color: #ffebee;
      }

      .output.success {
        color: var(--success-color);
        background-color: #e8f5e9;
      }

      .status-indicator {
        display: inline-block;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        margin-right: 8px;
      }

      .status-indicator.success {
        background-color: var(--success-color);
      }

      .status-indicator.error {
        background-color: var(--error-color);
      }

      .status-indicator.loading {
        background-color: var(--warning-color);
        animation: pulse 1.5s infinite;
      }

      @keyframes pulse {
        0% {
          opacity: 1;
        }
        50% {
          opacity: 0.4;
        }
        100% {
          opacity: 1;
        }
      }

      .example-description {
        color: #666;
        font-size: 0.9rem;
        margin-bottom: 1rem;
      }

      .tabs {
        display: flex;
        border-bottom: 1px solid var(--border-color);
        margin-bottom: 1rem;
      }

      .tab {
        padding: 0.5rem 1rem;
        cursor: pointer;
        border-bottom: 2px solid transparent;
      }

      .tab.active {
        border-bottom: 2px solid var(--primary-color);
        color: var(--primary-color);
        font-weight: bold;
      }

      .tab-content {
        display: none;
      }

      .tab-content.active {
        display: block;
      }

      .back-link {
        display: inline-block;
        margin-bottom: 1rem;
        color: var(--primary-color);
        text-decoration: none;
      }

      .back-link:hover {
        text-decoration: underline;
      }
    </style>
  </head>
  <body>
    <div class="header">
      <h1>Fetcher EventStream Examples</h1>
      <p>
        Interactive examples demonstrating Server-Sent Events (SSE) with Fetcher
      </p>
      <a href="/" class="back-link">← Back to Main Examples</a>
    </div>

    <div class="container">
      <!-- Basic SSE Example -->
      <div class="example-card">
        <h2>Basic Server-Sent Events</h2>
        <div class="example-description">
          Demonstrates connecting to a Server-Sent Events stream and processing
          real-time updates.
        </div>
        <div class="controls">
          <button id="basicSseBtn">Connect to SSE Stream</button>
          <button id="basicSseStopBtn">Disconnect</button>
          <button id="basicSseClearBtn" class="secondary">Clear Output</button>
        </div>
        <div id="basicSseOutput" class="output">
          Click "Connect to SSE Stream" to start receiving events
        </div>
      </div>

      <!-- SSE with Custom Events Example -->
      <div class="example-card">
        <h2>SSE with Custom Events</h2>
        <div class="example-description">
          Shows how to handle different types of events in an SSE stream.
        </div>
        <div class="controls">
          <button id="customSseBtn">Connect with Custom Events</button>
          <button id="customSseStopBtn">Disconnect</button>
          <button id="customSseClearBtn" class="secondary">Clear Output</button>
        </div>
        <div id="customSseOutput" class="output">
          Click "Connect with Custom Events" to start receiving different event
          types
        </div>
      </div>

      <!-- SSE Error Handling Example -->
      <div class="example-card">
        <h2>SSE Error Handling</h2>
        <div class="example-description">
          Demonstrates how to handle connection errors and reconnections in SSE
          streams.
        </div>
        <div class="controls">
          <button id="errorSseBtn">Connect with Error Handling</button>
          <button id="errorSseStopBtn">Disconnect</button>
          <button id="errorSseClearBtn" class="secondary">Clear Output</button>
        </div>
        <div id="errorSseOutput" class="output">
          Click "Connect with Error Handling" to see error handling in action
        </div>
      </div>

      <!-- SSE with Interceptor Example -->
      <div class="example-card">
        <h2>SSE with Interceptor</h2>
        <div class="example-description">
          Shows how to use the EventStreamInterceptor to automatically add
          eventStream() method to responses.
        </div>
        <div class="controls">
          <button id="interceptorSseBtn">Connect with Interceptor</button>
          <button id="interceptorSseStopBtn">Disconnect</button>
          <button id="interceptorSseClearBtn" class="secondary">
            Clear Output
          </button>
        </div>
        <div id="interceptorSseOutput" class="output">
          Click "Connect with Interceptor" to see automatic SSE handling
        </div>
      </div>
    </div>

    <script type="module">
      // Import the actual Fetcher and EventStreamInterceptor
      import { Fetcher } from '../packages/fetcher/dist/index.es.js';
      import { EventStreamInterceptor } from '../packages/eventstream/dist/index.es.js';

      // Create a real Fetcher instance
      const fetcher = new Fetcher({
        baseURL: 'http://localhost:3001',
      });

      // Add the event stream interceptor
      fetcher.interceptors.response.use(new EventStreamInterceptor());

      // Helper function to append to output
      function appendOutput(elementId, content, className = '') {
        const outputElement = document.getElementById(elementId);
        outputElement.textContent += content + '\n';
        outputElement.className = `output ${className}`;
        // Scroll to bottom
        outputElement.scrollTop = outputElement.scrollHeight;
      }

      // Helper function to update output
      function updateOutput(elementId, content, className = '') {
        const outputElement = document.getElementById(elementId);
        outputElement.textContent = content;
        outputElement.className = `output ${className}`;
      }

      // Helper function to show loading state
      function showLoading(elementId) {
        updateOutput(elementId, 'Connecting to SSE stream...', 'loading');
      }

      // Basic SSE Example
      let basicSseController = null;

      document
        .getElementById('basicSseBtn')
        .addEventListener('click', async () => {
          const outputId = 'basicSseOutput';
          showLoading(outputId);

          try {
            // Connect to the real SSE endpoint
            const response = await fetcher.get('/events');

            if (response.eventStream) {
              appendOutput(outputId, 'Connected to SSE stream', 'success');

              // Create an async iterator for the event stream
              basicSseController = true;

              // Process events asynchronously
              (async () => {
                try {
                  for await (const event of response.eventStream()) {
                    if (!basicSseController) break;

                    const eventData = {
                      type: event.event || 'message',
                      id: event.id || 'unknown',
                      data: event.data,
                      timestamp: new Date().toISOString(),
                    };

                    appendOutput(
                      outputId,
                      `[${eventData.timestamp}] ${eventData.type}: ${eventData.data}`,
                      'success',
                    );
                  }
                } catch (error) {
                  if (basicSseController) {
                    appendOutput(
                      outputId,
                      `SSE Error: ${error.message}`,
                      'error',
                    );
                  }
                }
              })();
            } else {
              updateOutput(
                outputId,
                'Response is not an event stream',
                'error',
              );
            }
          } catch (error) {
            updateOutput(
              outputId,
              `Connection Error: ${error.message}`,
              'error',
            );
          }
        });

      document
        .getElementById('basicSseStopBtn')
        .addEventListener('click', () => {
          const outputId = 'basicSseOutput';
          if (basicSseController) {
            basicSseController = null;
            appendOutput(outputId, '\nSSE stream stopped.', 'success');
          } else {
            updateOutput(outputId, 'No active SSE stream to stop.', 'error');
          }
        });

      document
        .getElementById('basicSseClearBtn')
        .addEventListener('click', () => {
          basicSseController = null;
          updateOutput(
            'basicSseOutput',
            'Click "Connect to SSE Stream" to start receiving events',
          );
        });

      // Custom Events Example
      let customSseController = null;

      document
        .getElementById('customSseBtn')
        .addEventListener('click', async () => {
          const outputId = 'customSseOutput';
          showLoading(outputId);

          try {
            // Connect to the real SSE endpoint
            const response = await fetcher.get('/events');

            if (response.eventStream) {
              appendOutput(
                outputId,
                'Connected to SSE stream with custom events',
                'success',
              );

              // Create an async iterator for the event stream
              customSseController = true;

              // Process events asynchronously
              (async () => {
                try {
                  for await (const event of response.eventStream()) {
                    if (!customSseController) break;

                    const eventData = {
                      type: event.event || 'message',
                      id: event.id || 'unknown',
                      data: event.data,
                      timestamp: new Date().toISOString(),
                    };

                    // Handle different event types
                    switch (eventData.type) {
                      case 'update':
                        appendOutput(
                          outputId,
                          `[${eventData.timestamp}] UPDATE #${eventData.id}: ${eventData.data}`,
                          'success',
                        );
                        break;
                      case 'message':
                        appendOutput(
                          outputId,
                          `[${eventData.timestamp}] MESSAGE #${eventData.id}: ${eventData.data}`,
                          'success',
                        );
                        break;
                      case 'notification':
                        appendOutput(
                          outputId,
                          `[${eventData.timestamp}] NOTIFICATION #${eventData.id}: ${eventData.data}`,
                          'success',
                        );
                        break;
                      default:
                        appendOutput(
                          outputId,
                          `[${eventData.timestamp}] EVENT #${eventData.id}: ${eventData.data}`,
                          'success',
                        );
                    }
                  }
                } catch (error) {
                  if (customSseController) {
                    appendOutput(
                      outputId,
                      `SSE Error: ${error.message}`,
                      'error',
                    );
                  }
                }
              })();
            } else {
              updateOutput(
                outputId,
                'Response is not an event stream',
                'error',
              );
            }
          } catch (error) {
            updateOutput(
              outputId,
              `Connection Error: ${error.message}`,
              'error',
            );
          }
        });

      document
        .getElementById('customSseStopBtn')
        .addEventListener('click', () => {
          const outputId = 'customSseOutput';
          if (customSseController) {
            customSseController = null;
            appendOutput(
              outputId,
              '\nCustom events stream stopped.',
              'success',
            );
          } else {
            updateOutput(
              outputId,
              'No active custom events stream to stop.',
              'error',
            );
          }
        });

      document
        .getElementById('customSseClearBtn')
        .addEventListener('click', () => {
          customSseController = null;
          updateOutput(
            'customSseOutput',
            'Click "Connect with Custom Events" to start receiving different event types',
          );
        });

      // Error Handling Example
      let errorSseController = null;

      document
        .getElementById('errorSseBtn')
        .addEventListener('click', async () => {
          const outputId = 'errorSseOutput';
          showLoading(outputId);

          try {
            // Connect to the real SSE endpoint
            const response = await fetcher.get('/events');

            if (response.eventStream) {
              appendOutput(
                outputId,
                'Connected to SSE stream with error handling',
                'success',
              );

              // Create an async iterator for the event stream
              errorSseController = true;

              // Process events asynchronously with error handling
              (async () => {
                try {
                  for await (const event of response.eventStream()) {
                    if (!errorSseController) break;

                    const eventData = {
                      type: event.event || 'message',
                      id: event.id || 'unknown',
                      data: event.data,
                      timestamp: new Date().toISOString(),
                    };

                    appendOutput(
                      outputId,
                      `[${eventData.timestamp}] ${eventData.type}: ${eventData.data}`,
                      'success',
                    );
                  }
                } catch (error) {
                  if (errorSseController) {
                    appendOutput(
                      outputId,
                      `[ERROR] Connection error: ${error.message}`,
                      'error',
                    );

                    // Simulate reconnection attempt
                    setTimeout(() => {
                      if (errorSseController) {
                        appendOutput(
                          outputId,
                          '[INFO] Attempting to reconnect...',
                          'loading',
                        );
                        // In a real implementation, you would try to reconnect here
                      }
                    }, 3000);
                  }
                }
              })();
            } else {
              updateOutput(
                outputId,
                'Response is not an event stream',
                'error',
              );
            }
          } catch (error) {
            updateOutput(
              outputId,
              `Connection Error: ${error.message}`,
              'error',
            );
          }
        });

      document
        .getElementById('errorSseStopBtn')
        .addEventListener('click', () => {
          const outputId = 'errorSseOutput';
          if (errorSseController) {
            errorSseController = null;
            appendOutput(
              outputId,
              '\nError handling stream stopped.',
              'success',
            );
          } else {
            updateOutput(
              outputId,
              'No active error handling stream to stop.',
              'error',
            );
          }
        });

      document
        .getElementById('errorSseClearBtn')
        .addEventListener('click', () => {
          errorSseController = null;
          updateOutput(
            'errorSseOutput',
            'Click "Connect with Error Handling" to see error handling in action',
          );
        });

      // Interceptor Example
      let interceptorSseController = null;

      document
        .getElementById('interceptorSseBtn')
        .addEventListener('click', async () => {
          const outputId = 'interceptorSseOutput';
          showLoading(outputId);

          try {
            // Connect to the real SSE endpoint
            // The EventStreamInterceptor is already registered
            const response = await fetcher.get('/events');

            if (response.eventStream) {
              appendOutput(
                outputId,
                '[INFO] EventStreamInterceptor automatically added eventStream() method to response',
                'success',
              );
              appendOutput(
                outputId,
                'Connected to SSE stream through interceptor',
                'success',
              );

              // Create an async iterator for the event stream
              interceptorSseController = true;

              // Process events asynchronously
              (async () => {
                try {
                  for await (const event of response.eventStream()) {
                    if (!interceptorSseController) break;

                    const eventData = {
                      type: event.event || 'message',
                      id: event.id || 'unknown',
                      data: event.data,
                      timestamp: new Date().toISOString(),
                    };

                    appendOutput(
                      outputId,
                      `[${eventData.timestamp}] [INTERCEPTED] ${eventData.type}: ${eventData.data}`,
                      'success',
                    );
                  }
                } catch (error) {
                  if (interceptorSseController) {
                    appendOutput(
                      outputId,
                      `[ERROR] Interceptor processing error: ${error.message}`,
                      'error',
                    );
                  }
                }
              })();
            } else {
              updateOutput(
                outputId,
                'Response is not an event stream (Interceptor may not have been applied)',
                'error',
              );
            }
          } catch (error) {
            updateOutput(
              outputId,
              `Connection Error: ${error.message}`,
              'error',
            );
          }
        });

      document
        .getElementById('interceptorSseStopBtn')
        .addEventListener('click', () => {
          const outputId = 'interceptorSseOutput';
          if (interceptorSseController) {
            interceptorSseController = null;
            appendOutput(outputId, '\nInterceptor stream stopped.', 'success');
          } else {
            updateOutput(
              outputId,
              'No active interceptor stream to stop.',
              'error',
            );
          }
        });

      document
        .getElementById('interceptorSseClearBtn')
        .addEventListener('click', () => {
          interceptorSseController = null;
          updateOutput(
            'interceptorSseOutput',
            'Click "Connect with Interceptor" to see automatic SSE handling',
          );
        });
    </script>
  </body>
</html>
